package de.sendorian.app.forumArchive.finder;

import static de.sendorian.util.matcher.FileTypeMatchers.archive;
import static org.apache.commons.lang.StringEscapeUtils.unescapeHtml;
import static org.apache.commons.lang.StringUtils.trim;
import static org.apache.commons.lang.StringUtils.trimToNull;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.anything;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.startsWith;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.annotation.PostConstruct;

import org.apache.log4j.Logger;
import org.hamcrest.Matcher;
import org.htmlparser.Node;
import org.htmlparser.filters.TagNameFilter;
import org.htmlparser.nodes.TagNode;
import org.htmlparser.util.NodeList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import de.sendorian.app.forumArchive.domain.Archive;
import de.sendorian.app.forumArchive.domain.Image;
import de.sendorian.app.forumArchive.domain.Link;
import de.sendorian.app.forumArchive.domain.Post;
import de.sendorian.app.forumArchive.domain.Topic;
import de.sendorian.app.forumArchive.domain.User;
import de.sendorian.app.forumArchive.repository.UserRepository;

/**
 * Finds a post and all data needed to initialize the post.
 * 
 * @author sendorian
 *
 */
@Component
public abstract class PostFinder extends FinderBase<Topic, Post> {

    private static final Logger LOG = Logger.getLogger(PostFinder.class);

    protected Matcher<Post> matcher = anything();
    protected boolean recursiveSearchDisabled = false;

    protected UserRepository userRepository;
    protected @Value("#{config.baseUrl}") String baseUrl;
    protected Matcher<String> externalLinkMatcher;
    protected Matcher<String> internalLinkMatcher;


    public PostFinder() {
    }


    public PostFinder(Matcher<Post> matcher) {
        this.matcher = matcher;
    }
    
    
    /**
     * Fill linkMatcher after all attributes are injected.
     */
    @PostConstruct
    protected void init() {
        internalLinkMatcher = allOf(is(not(archive())), startsWith(baseUrl), not(containsString("mailto")));
        externalLinkMatcher = allOf(is(not(archive())), not(startsWith(baseUrl)), not(containsString("mailto")));
    }


    public List<Post> find(Topic topic) {
        return find(topic, topic.getUrl());
    }


    public List<Post> find(Topic topic, String url) {
        NodeList tableRows = parse(url, new TagNameFilter("tr"), false);
        return find(topic, tableRows);
    }
    
    
    public List<Post> find(Topic topic, NodeList tableRows) {
        List<Post> posts = new ArrayList<Post>();

        LOG.debug("Found posts in '" + topic.getName() + "':");
        for (Node node : tableRows.toNodeArray()) {
            TagNode row = (TagNode) node;
            String author = getAuthor(row);

            if (author != null) {
                Post post = new Post();
                post.setPostDate(getPostDate(row));
                post.setLastEditedDate(getLastEditedDate(row));
                String userName = trimToNull(unescapeHtml(author));
                if (userName != null) {
                    User user = userRepository.findByName(userName);
                    if (user == null) {
                        user = new User(userName);
                        userRepository.save(user);
                    }
                    post.setAuthor(user);
                }
                post.setMessage(trim(unescapeHtml(getMessage(row))));
                post.setFooter(trim(unescapeHtml(getFooter(row))));
                post.setImages(getImages(row));
                post.setArchives(getArchives(row));
                post.setLinks(getLinks(row));

                if (matcher.matches(post)) {
                    posts.add(post);
                    LOG.debug(post.toString());
                }
            }
        }

        if (!recursiveSearchDisabled) {
            String prevPage = getPreviousPage(tableRows);
            if (prevPage != null) {
                List<Post> prevPosts = find(topic, prevPage);
                prevPosts.addAll(posts);
                posts = prevPosts;
            }
        }

        LOG.info("Found " + posts.size() + " posts in '" + topic.getName() + "'");
        return posts;
    }


    public Matcher<String> getExternalLinkMatcher() {
        return externalLinkMatcher;
    }


    public Matcher<String> getInternalLinkMatcher() {
        return internalLinkMatcher;
    }


    abstract protected String getAuthor(TagNode tr);


    abstract protected Date getPostDate(TagNode tr);
    
    
    abstract protected Date getLastEditedDate(TagNode tr);


    abstract protected String getMessage(TagNode tr);
    
    
    abstract protected String getFooter(TagNode tr);


    protected abstract List<Archive> getArchives(TagNode row);


    protected abstract List<Image> getImages(TagNode row);


    protected abstract List<Link> getLinks(TagNode row);


    abstract protected String getPreviousPage(NodeList tableRows);


    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    
    
    public void enableRecursiveSearch() {
        recursiveSearchDisabled = false;
    }
    
    
    public void disableRecursiveSearch() {
        recursiveSearchDisabled = true;
    }
}
